package com.xs.ahoi.hcxs.service;

import static com.xs.ahoi.hcxs.utils.StringUtils.isEmpty;
import static com.xs.ahoi.hcxs.utils.StringUtils.isNotEmpty;
import static com.xs.ahoi.hcxs.utils.StringUtils.replaceWrap;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.v4.content.ContextCompat;

import com.xs.ahoi.hcxs.Ahoi;
import com.xs.ahoi.hcxs.R;
import com.xs.ahoi.hcxs.dao.BookDao;
import com.xs.ahoi.hcxs.dao.CatalogDao;
import com.xs.ahoi.hcxs.dao.ConfigDao;
import com.xs.ahoi.hcxs.model.Book;
import com.xs.ahoi.hcxs.model.Catalog;
import com.xs.ahoi.hcxs.model.vo.CharData;
import com.xs.ahoi.hcxs.model.vo.PageCharData;
import com.xs.ahoi.hcxs.model.vo.PageVo;
import com.xs.ahoi.hcxs.model.vo.SearchDataVo;
import com.xs.ahoi.hcxs.ui.view.BookContentView;
import com.xs.ahoi.hcxs.utils.Constants;
import com.xs.ahoi.hcxs.utils.DateUtil;
import com.xs.ahoi.hcxs.utils.ObjectUtils;
import com.xs.ahoi.hcxs.utils.SearchUtil;
import com.xs.ahoi.hcxs.utils.ThreadPoolFactory;
import com.xs.ahoi.hcxs.utils.ToastUtil;

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * author: ahoi
 * date: 2018/6/19 17:49
 * description:
 */
public class BookFactory {

    private static BookFactory instance;

    // 书籍目录
    private static List<Catalog> catalogs;
    // 页面下标和文字集合封装类
    public PageVo currentPageVo, prevPageVo, tempPageVo;
    // 电池电量
    public int powerPercent;
    private BookDao bookDao;
    private CatalogDao catalogDao;
    private ConfigDao configDao;
    // 自定义内容view
    private BookContentView contentView;
    // 文件内存映射方案，读写性能极高
    private MappedByteBuffer mappedByteBuffer;
    // 随机流文件
    private RandomAccessFile raf;
    // 屏幕尺寸
    private float screenWidth, screenHeight;
    // 文字区域尺寸
    private float visibleWidth, visibleHeight;
    // 文字最大行数
    private int maxLineNumber;
    // 本页文字
    private List<String> pagelines;
    // 文件总长度
    private long totalLength;
    // 正则匹配章节目录
    private Pattern pattern = Pattern.compile(Constants.REGEX_PATTERN_CATEGORY);
    // 本次播放的文字行集合中的下标
    private List<Integer> ttsPlayLineIndexs;
    // 正文画笔
    private Paint mTextPaint;
    // 标题下的横线画笔
    private Paint mLinePaint;
    // 标题画笔
    private Paint mTitlePaint;
    // 选中文字画笔
    private Paint mSelectPaint;
    // 背景画笔
    private Paint mBgPaint;
    // 书籍ID
    private Book book;
    private int bookId;
    private String bookPath;
    // 书籍编码
    private String charset;
    // 书籍标题
    private String bookTitle;
    // 本章标题
    private String catalogName, catalogNameTemp;
    // 字体
    private float textSize;
    private float titleSize = Constants.DEFAULT_TITLE_TEXTSIZE;
    // 文字颜色
    private int textColor;
    private int titleColor;
    private int textColorHighLight = Constants.CONTENT_HIGHLIGHT_COLOR;
    // 背景色
    private int bgColor;
    // 行距
    private float rowSpacing;
    // 文字上下左右边距
    private float marginTop = Constants.DEFAULT_CONTENT_TOP;
    private float marginBottom = Constants.DEFAULT_CONTENT_BOTTOM;
    private float marginLeft = Constants.DEFAULT_CONTENT_LEFT;
    private float marginRight = Constants.DEFAULT_CONTENT_RIGHT;
    private float msgMarginTop = Constants.DEFAULT_MSG_TOP;
    private float msgMarginBottom = Constants.DEFAULT_MSG_BOTTOM;
    private float powerWidth = Constants.POWER_WIDTH;
    private float powerHeight = Constants.POWER_HEIGHT;
    private float power2Width = Constants.POWER2_WIDTH;
    private float power2Height = Constants.POWER2_HEIGHT;
    private float powerPadding = Constants.POWER_PADDING;
    private float powerBorder = Constants.POWER_BORDER;
    // 页面字符数据集合，用于长按选中
    private List<PageCharData> pageCharDatas = new ArrayList<>();
    private PageCharData pageCharData;
    // 标记本行文字是否为标题
    private boolean isTitleLine;
    // 本次是否取消翻页
    boolean isCancelPage = false;
    // 总页码
    private Integer pageTotal;
    // 当前页码
    private Integer pageNumber;
    // 页码显示的文字   当前页/总页码
    private String pageShowText, pagePercentShowText;


    // 创建新的factory
    public static synchronized BookFactory getInstance() {
        if (instance == null) {
            instance = new BookFactory();
        }
        return instance;
    }

    // 构造方法
    private BookFactory() {
        bookDao = BookDao.getInstance();
        catalogDao = CatalogDao.getInstance();
        configDao = ConfigDao.getInstance();
        currentPageVo = new PageVo();
        prevPageVo = new PageVo();
        tempPageVo = new PageVo();
    }

    // 初始化
    public BookFactory init() {

        // 获取缓存中保存的字体参数
        textSize = configDao.getTextSize();

        // 初始化背景
        String[] backgroundRes = Constants.backgroundColors[configDao.getBackgroundIndex()];
        bgColor = Color.parseColor(backgroundRes[0]);
        textColor = Color.parseColor(backgroundRes[1]);
        titleColor = Color.parseColor(backgroundRes[2]);

        // 背景画笔
        mBgPaint = new Paint();
        mBgPaint.setAntiAlias(true);
        mBgPaint.setStyle(Paint.Style.FILL);
        // 选中文字画笔
        mSelectPaint = new Paint();
        mSelectPaint.setAntiAlias(true);
        mSelectPaint.setStyle(Paint.Style.STROKE);
        mSelectPaint.setStrokeWidth(1f);
        mSelectPaint.setTextSize(textSize);
        mSelectPaint.setColor(ContextCompat.getColor(Ahoi.getInstance().getActivity(), R.color.colorRed));
        // 初始化正文画笔
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(textSize);
        // 初始化标题下的横线画笔
        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setTextSize(textSize);
        mLinePaint.setStrokeWidth(2f);
        // 初始化标题画笔
        mTitlePaint = new Paint();
        mTitlePaint.setAntiAlias(true);
        mTitlePaint.setFakeBoldText(true);
        mTitlePaint.setTextSize(titleSize);

        // 计算文字最大行数
        rowSpacing = configDao.getRowSpacing();
        maxLineNumber = (int) (visibleHeight / (mTextPaint.getFontSpacing() + rowSpacing));

        return this;
    }

    // 正式开始画内容的方法
    public void doDraw(Canvas canvas) {
        canvas.drawColor(bgColor);

        mTitlePaint.setColor(titleColor);
        mLinePaint.setColor(textColor);

        // 画正文
        pagelines = currentPageVo.getLines();
        isTitleLine = false;
        if (ObjectUtils.isEmpty(pagelines)) {
            return;
        }

        // 获取当前位置的标题
        catalogName = getCatalog(currentPageVo.getMbbStart());

        for (int i = 0; i < pagelines.size(); i++) {
            String line = pagelines.get(i);

            // 设置当前正在播放的文字高亮颜色
            if (ttsPlayLineIndexs != null && ttsPlayLineIndexs.contains(i)) {
                mTextPaint.setColor(textColorHighLight);
            } else {
                mTextPaint.setColor(textColor);
            }

            float y = marginTop + (mTextPaint.getFontSpacing() + rowSpacing) * (i + 1);

            // 如果文字就等于章节标题，就直接加粗并画线
            if (!isTitleLine && isNotEmptyLineAndCatalog(line, catalogName) && catalogName.equals(replaceWrap(line).trim())) {
                // 画标题【加粗】
                drawTitleBold(canvas, mTextPaint, line, marginLeft, y);
                // 画章节标题下的横线
                drawTitleLine(canvas, mLinePaint, marginLeft, y, screenWidth - marginRight, y);
            }
            // 如果文字是标题的开头，就只加粗，并标记标题未完
            else if (!isTitleLine && isNotEmptyLineAndCatalog(line, catalogName) && catalogName.startsWith(replaceWrap(line).trim())) {
                isTitleLine = true;
                // 画标题【加粗】
                drawTitleBold(canvas, mTextPaint, line, marginLeft, y);
            }
            // 如果标记为未完， 且文字是标题的结尾，就标记标题结束，加粗且画线
            else if (isTitleLine && isNotEmptyLineAndCatalog(line, catalogName) && catalogName.endsWith(replaceWrap(line).trim())) {
                isTitleLine = false;
                // 画标题【加粗】
                drawTitleBold(canvas, mTextPaint, line, marginLeft, y);
                // 画章节标题下的横线
                drawTitleLine(canvas, mLinePaint, marginLeft, y, screenWidth - marginRight, y);
            }
            // 如果标记为未完， 且文字是标题的中间部分，只加粗
            else if (isTitleLine && isNotEmptyLineAndCatalog(line, catalogName) && catalogName.contains(replaceWrap(line).trim())) {
                // 画标题【加粗】
                drawTitleBold(canvas, mTextPaint, line, marginLeft, y);
            }
            // 正文
            else {
                // 兼容标题只有一行时，标记未关闭的情况，则关闭标记且画线
                if (isTitleLine) {
                    isTitleLine = false;
                    // 画章节标题下的横线
                    drawTitleLine(canvas, mLinePaint, marginLeft, y, screenWidth - marginRight, y);
                }
                // 画正文
                canvas.drawText(line, marginLeft, y, mTextPaint);
            }
        }

        // 如果本页是章节标题开始的一页，则左上角显示为书名
        catalogNameTemp = catalogName;
        if (!isNotEmpty(catalogName) || (pagelines.size() > 0 && isNotEmpty(replaceWrap(pagelines.get(0).trim())) && catalogName.startsWith(replaceWrap(pagelines.get(0)).trim()))) {
            catalogNameTemp = bookTitle;
        }
        // 画本章节目录(左上)
        mTitlePaint.setTextAlign(Paint.Align.LEFT);
        int index = mTitlePaint.breakText(catalogNameTemp, true, visibleWidth, null);
        if (index > 1 && index < catalogNameTemp.length()) {
            catalogNameTemp = catalogNameTemp.substring(0, index - 1) + "...";
        }
        canvas.drawText(catalogNameTemp, marginLeft, titleSize + msgMarginTop, mTitlePaint);

        // 画阅读进度百分比(底右)
        mTitlePaint.setTextAlign(Paint.Align.RIGHT);
        String percent = ObjectUtils.toFix(currentPageVo.getMbbStart() * 100f / totalLength, 2) + "%";
        canvas.drawText(percent, screenWidth - marginRight, screenHeight - msgMarginBottom, mTitlePaint);

//        // 画分页页码(底中)
//        canvas.drawText(pageShowText, screenWidth / 2, screenHeight - marginBottom, mPaginationPaint);

        // 画电量时间(底左)
        drawPowerAndTime(canvas);

        contentView.postInvalidate();
    }

    // 初始化屏幕尺寸
    public BookFactory size(float screenWidth, float screenHeight) {
        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;

        // 可显示区域尺寸
        visibleWidth = screenWidth - (marginLeft + marginRight);
        visibleHeight = screenHeight - (marginTop + marginBottom);
        return this;
    }

    // 打开书籍
    public BookFactory open(int bookId) {
        this.bookId = bookId;
        book = bookDao.getById(bookId);
        bookPath = book.getPath();
        charset = configDao.getCharset(bookPath);
        int lastReadPosition = book.getLastReadPosition();
        int beginIndex = (lastReadPosition < 0 ? 0 : lastReadPosition);

        // 设置本页下标参数
        currentPageVo.setMbbStart(beginIndex);
        currentPageVo.setMbbEnd(beginIndex);

        // 获取书籍信息
        updateBookInfo();

        // 读取章节目录并保存
        startCatalogs(false);

        return this;
    }

    // 获取书籍信息
    public void updateBookInfo() {
        try {
            File file = new File(bookPath);
            bookTitle = file.getName();
            totalLength = file.length();
            raf = new RandomAccessFile(bookPath, "r");
            mappedByteBuffer = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, totalLength);

            // 更新书籍信息
            book.setTotalLenth(totalLength);
            book.setName(bookTitle);
            ThreadPoolFactory.getInstance().execute(() -> bookDao.updateInfo(bookId, book));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 画电量时间(底左)
    private void drawPowerAndTime(Canvas canvas) {
        mTitlePaint.setTextAlign(Paint.Align.LEFT);

        // 这里开始画电池边框，中间镂空不填充
        mTitlePaint.setStyle(Paint.Style.STROKE);
        mTitlePaint.setStrokeWidth(powerBorder);
        RectF borderRect = new RectF(
                marginLeft + 0,
                screenHeight - msgMarginBottom - powerHeight,
                marginLeft + powerWidth,
                screenHeight - msgMarginBottom);
        canvas.drawRoundRect(borderRect, 5, 5, mTitlePaint);

        // 画正极图标
        mTitlePaint.setStyle(Paint.Style.FILL);
        mTitlePaint.setStrokeWidth(0);
        RectF rightRect = new RectF(
                borderRect.right + 0,
                screenHeight - msgMarginBottom - power2Height * 2,
                borderRect.right + power2Width,
                screenHeight - msgMarginBottom - power2Height);
        canvas.drawRect(rightRect, mTitlePaint);

        // 画电量百分比
        RectF powerRect = new RectF(
                borderRect.left + powerPadding,
                borderRect.top + powerPadding,
                (borderRect.right - powerPadding * 2 - powerBorder - marginLeft) * (powerPercent / 100f) + marginLeft + powerPadding + powerBorder,
                borderRect.bottom - powerPadding);
        canvas.drawRect(powerRect, mTitlePaint);

        // 画时间(时:分)
        String time = DateUtil.date2String(DateUtil.getDate(), DateUtil.FORMAT_ONLY_HM);
        canvas.drawText(time, rightRect.right + 10, screenHeight - msgMarginBottom, mTitlePaint);

    }

    // 更新书籍的最后一次阅读时间和位置
    public void updateLastPosition() {
        ThreadPoolFactory.getInstance().execute(() -> bookDao.updateLastRead(bookId, currentPageVo.getMbbStart()));
    }

    // 改变字体大小 1-增加 0-减小
    public float changeTextSize(Constants.ChangeType type) {
        if (Constants.ChangeType.add.equals(type)) {
            if (textSize >= Constants.TEXT_SIZE_MAX) {
                return -1;
            }
            textSize += Constants.TEXT_SIZE_CHANGE_STEP;
        } else {
            if (textSize <= Constants.TEXT_SIZE_MIN) {
                return -1;
            }
            textSize -= Constants.TEXT_SIZE_CHANGE_STEP;
        }
        // 记录字体大小
        configDao.setTextSize(textSize);
        // 重新初始化页面
        init();
        return textSize;
    }

    // 改变行距
    public void changeRowSpacing(int margin) {
        configDao.setRowSpacing(margin);
        // 重新初始化页面
        init();
    }

    // 单独开启子线程，获取章节目录
    public synchronized void startCatalogs(boolean forceUpdate) {
        // 从数据库读取目录，如果有目录且文件长度没变化，则不再重新获取目录
        catalogs = catalogDao.listByBookId(bookId);
        if (!forceUpdate && catalogs.size() > 0 && totalLength == book.getTotalLenth()) {
            return;
        }

        ThreadPoolFactory.getInstance().execute(() -> {
            try {
                // 开始全文匹配章节目录
                List<Catalog> list = new ArrayList<>();
                Catalog catalog;

                // 小说断章时读取文字下标
                int mbbIndex_ = 0;
                byte[] paraByte;
                String paraText;
                String catalogName;
                while (mbbIndex_ < totalLength) {
                    // 读取一段文字
                    paraByte = readParaNext(mbbIndex_);
                    mbbIndex_ += paraByte.length;
                    paraText = new String(paraByte, charset);

                    // 正则匹配
                    catalogName = getTitle(paraText);
                    if (isNotEmpty(catalogName)) {
                        catalogName = catalogName.replaceAll(Constants.REGEX_PATTERN_SECTITLE, "").trim();
                        catalog = new Catalog();
                        catalog.setName(catalogName);
                        catalog.setPosition(mbbIndex_ - paraByte.length);
                        list.add(catalog);
                    }
                }

                // 重新断章完成后，再将章节目录保存入库
                catalogs.clear();
                catalogs.addAll(list);
                catalogDao.save(bookId, catalogs);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    // 全文搜索的下标
    public int searchMbbIndex = 0;
    // 存放搜索结果
    public static List<SearchDataVo> searchResult = new ArrayList<>();
    public static List<SearchDataVo> searchResultTemp = new ArrayList<>();

    // 开始搜索
    public List<SearchDataVo> startSearch(String str) {
        try {
            byte[] paraByte;
            String paraText;
            while (searchMbbIndex < totalLength) {
                // 读取一段文字
                paraByte = readParaNext(searchMbbIndex);
                paraText = new String(paraByte, charset).trim();

                if (paraText.length() > 0 && !paraText.equals("\r\n")) {
                    List<SearchDataVo> result = SearchUtil.search(searchMbbIndex, totalLength, paraText, str);
                    if (result != null) {
                        searchResultTemp.addAll(result);
                        // 每超过50条结果就停止本次搜索
                        int size = (searchResult.size() / 50 + 1) * 50;
                        if (searchResultTemp.size() >= size) {
                            searchResult.clear();
                            for (int i = 0; i < size; i++) {
                                searchResult.add(searchResultTemp.get(i));
                            }
                            break;
                        }
                    }
                }
                searchMbbIndex += paraByte.length;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (canSearchContinue()) {
            return searchResult;
        }
        return searchResultTemp;
    }

    // 重新开始搜索
    public List<SearchDataVo> reStartSearch(String str) {
        searchResult.clear();
        searchResultTemp.clear();
        searchMbbIndex = 0;
        return startSearch(str);
    }

    // 是否能继续搜索
    public boolean canSearchContinue() {
        return searchMbbIndex < totalLength;
    }

    //**********************************************************
    //********************READ 相关的方法**************************
    //**********************************************************

    // 翻到下一页，直接跳转到指定位置
    public void pageNext(Integer mbbIndex) {

        // 把结束下标赋值给开始下标，然后结束下标往后走
        int mbbIndexEnd = mbbIndex == null ? currentPageVo.getMbbEnd() : mbbIndex;
        currentPageVo.setMbbEnd(mbbIndexEnd);
        currentPageVo.setMbbStart(mbbIndexEnd);

        // 开始读一页文本，并记录结束后的下标
        currentPageVo.setLines(readTextNext());

        // 新开线程更新最后一次阅读位置
        updateLastPosition();
    }

    // 翻到上一页
    public void pagePrev() {

        // 开始下标赋值给结束下标，然后开始下标往前走
        currentPageVo.setMbbEnd(currentPageVo.getMbbStart());
        currentPageVo.setLines(readTextPrev());

        // 新开线程更新最后一次阅读位置
        updateLastPosition();
    }

    // 获取下一页的文字内容，每行为单位
    private List<String> readTextNext() {
        return readTextNext(null);
    }

    // 获取下一页的文字内容，每行为单位
    private List<String> readTextNext(List<String> lines) {
        lines = lines == null ? new ArrayList<>() : lines;
        try {
            // 若本页行数小于最大行数，则继续读取下一段落
            int mbbIndexEnd = currentPageVo.getMbbEnd();
//            // 获取有效的下标，非乱码
//            mbbIndexEnd = FileUtil.getValidPosition(mbbIndexEnd);
            while (lines.size() < maxLineNumber && mbbIndexEnd < totalLength) {

                // 读取一段并转换为string
                byte[] bytes = readParaNext(mbbIndexEnd);
                String onePara = new String(bytes, charset);

                // 如果遇到章节标题，则停止，新章节单独一页开始显示
                if (lines.size() > 0 && isNotEmpty(getTitle(onePara))) {
                    break;
                }

                // 刷新下次读取时的下标
                mbbIndexEnd += bytes.length;

                while (onePara.length() > 0) {
                    // 测量一行显示的文字数，返回下标
                    int index = mTextPaint.breakText(onePara, true, visibleWidth, null);

                    // 将本行文字存入集合
                    lines.add(onePara.substring(0, index));

                    // 该段文字截取后的剩余
                    onePara = onePara.substring(index);

                    // 超出最大行数则停止
                    if (lines.size() >= maxLineNumber) {
                        break;
                    }
                }

                // 若截取后还有剩余，则刷新截取下标
                if (onePara.length() > 0) {
                    mbbIndexEnd -= onePara.getBytes(charset).length;
                }
            }
            currentPageVo.setMbbEnd(mbbIndexEnd);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return lines;
    }

    // 获取上一页的文字内容，每行为单位
    private List<String> readTextPrev() {
        List<String> lines = new ArrayList<>();
        try {
            boolean hasTitle = false;
            int mbbIndexStart = currentPageVo.getMbbStart();
//            // 获取有效的下标，非乱码
//            mbbIndexStart = FileUtil.getValidPosition(mbbIndexStart);
            // 若本页行数小于最大行数，则继续读取上一段落
            while (lines.size() < maxLineNumber && mbbIndexStart > 0 && !hasTitle) {

                // 读取一段并转换为string
                byte[] bytes = readParaPrev(mbbIndexStart);
                String onePara = new String(bytes, charset);

                // 如果遇到章节标题，则标记本次操作后停止往前读取，转而往后读取
                if (isNotEmpty(getTitle(onePara))) {
                    hasTitle = true;
                }

                // 刷新下次读取时的下标
                mbbIndexStart -= bytes.length;

                List<String> tempLine = new ArrayList<>();
                while (onePara.length() > 0) {
                    // 测量一行显示的文字数，返回下标
                    int index = mTextPaint.breakText(onePara, true, visibleWidth, null);

                    // 将本行文字存入集合
                    tempLine.add(onePara.substring(0, index));

                    // 该段文字截取后的剩余
                    onePara = onePara.substring(index);
                }

                // 逆序读取时，每读一段都放在list的最前面
                lines.addAll(0, tempLine);

                // 若已经读到开始位置，且本页文字总长度未满，则转向向后读取文字
                if (mbbIndexStart == 0 && lines.size() < maxLineNumber) {
                    // 此时一定不会超出最大行数，因为readTextNext的内层while做了判断
                    readTextNext(lines);
                }

                // 从第一行开始截取超出长度的文字
                while (lines.size() > maxLineNumber) {
                    mbbIndexStart += lines.get(0).getBytes(charset).length;
                    lines.remove(0);
                }

                // 如果本次往前读取遇到了章节标题，则转而往后读取
                if (hasTitle && lines.size() < maxLineNumber) {
                    readTextNext(lines);
                }
            }
            currentPageVo.setMbbStart(mbbIndexStart);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return lines;
    }

    // 获取下一段落的文字内容字节数组
    private byte[] readParaNext(int mbbIndex) {

        byte oneByte = 0;
        int i = mbbIndex;

        // 读到换行符（0x0a）为止
        while (oneByte != 0x0a && i < totalLength) {
            oneByte = mappedByteBuffer.get(i);
            i++;
        }

        // 根据下标重新读取本段文字（i绝对会大于 mbbIndex）
        int byteSize = i - mbbIndex;
        byte[] paraBytes = new byte[byteSize];
        for (int j = 0; j < byteSize; j++) {
            paraBytes[j] = mappedByteBuffer.get(j + mbbIndex);
        }
        return paraBytes;
    }

    // 获取上一段落的文字内容字节数组
    private byte[] readParaPrev(int mbbIndex) {

        byte oneByte = 0;
        // 读到换行符（0x0a）为止
        int i = mbbIndex - 1;
        while (i > 0) {
            oneByte = mappedByteBuffer.get(i);
            if (oneByte == 0x0a && i != mbbIndex - 1) {
                i++;
                break;
            }
            i--;
        }

        // i最小为0
        i = i < 0 ? 0 : i;

        // 根据下标重新读取本段文字（i绝对会小于 mbbIndex）
        int byteSize = mbbIndex - i;
        byte[] paraBytes = new byte[byteSize];
        for (int j = 0; j < byteSize; j++) {
            paraBytes[j] = mappedByteBuffer.get(i + j);
        }
        return paraBytes;
    }

    // 画标题【加粗】
    private void drawTitleBold(Canvas canvas, Paint paint, String text, float x, float y) {
        paint.setFakeBoldText(true);
        canvas.drawText(text, x, y, paint);
        paint.setFakeBoldText(false);
    }

    // 画章节标题下的横线
    private void drawTitleLine(Canvas canvas, Paint paint, float startx, float starty, float stopx, float stopy) {
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        canvas.drawLine(startx, starty + fontMetrics.bottom, stopx, stopy + fontMetrics.bottom, paint);
    }


    //**********************************************************
    //********************get set 方法**************************
    //**********************************************************

    // 判断拖动时坐标是否在文字区域内
    public boolean validDragMove(float touchX, float touchY) {
        return touchX >= marginLeft && touchX <= screenWidth - marginRight
                && touchY >= marginTop && touchY <= screenHeight - marginBottom;
    }

    // 标题和本行文字不为空
    private boolean isNotEmptyLineAndCatalog(String line, String catalog) {
        return isNotEmpty(replaceWrap(line).trim()) && isNotEmpty(catalog);
    }

    // 设置文字颜色
    public void setTextColor(int color) {
        this.textColor = color;
    }

    // 设置标题颜色
    public void setTitleColor(int color) {
        this.titleColor = color;
    }

    // 是否为第一页
    public boolean isPageFirst() {
        return currentPageVo.getMbbStart() <= 0;
    }

    // 是否为最后一页
    public boolean isPageLast() {
        return currentPageVo.getMbbEnd() >= totalLength;
    }

    // 设置电池电量百分比
    public void setPowerPercent(int powerPercent) {
        this.powerPercent = powerPercent;
    }

    // 获取页面当前位置的标题
    private String getCatalog(int position) {
        return catalogDao.getNameByPosition(bookId, position);
    }

    // 获取字体大小
    public String getTextSize() {
        return ObjectUtils.toInteger(textSize / 4) + "";
    }

    // 验证该行文字是否为标题，返回标题
    private String getTitle(String text) {
        String result = "";
        if (text.length() < Constants.CONTENT_TITLE_MAX_SIZE) {
            Matcher matcher = pattern.matcher(text);
            if (matcher.find()) {
                result = isEmpty(matcher.group()) ? "" : matcher.group();
            }
        }
        return result;
    }

    // 获取本页开始下标
    public int getMbbIndexStart() {
        return currentPageVo.getMbbStart();
    }

    // 获取随机流文件
    public RandomAccessFile getRaf() {
        return raf;
    }

    // 获取文件总长度
    public long getTotalLength() {
        return totalLength;
    }

    // 获取本页内容
    public List<String> getPagelines() {
        return currentPageVo.getLines();
    }

    // 获取本页文字字符集合
    public List<PageCharData> getPageCharDatas() {
        if (ObjectUtils.isEmpty(pageCharDatas)) {
            List<String> pagelines = getPagelines();
            for (int i = 0; i < pagelines.size(); i++) {
                // 获取本行文字的字符数据集合
                float y = marginTop + (mTextPaint.getFontSpacing() + rowSpacing) * (i + 1);
                pageCharData = new PageCharData();
                pageCharData.setCharData(CharData.getCharDatas(pagelines.get(i), mSelectPaint, (int) marginLeft, (int) y));
                pageCharDatas.add(pageCharData);
            }
        }
        return pageCharDatas;
    }

    // 获取本页选中的文字
    public String getPageCharSelected() {
        StringBuilder sb = new StringBuilder();
        for (PageCharData charData : pageCharDatas) {
            List<CharData> rowData = charData.getCharData();
            for (CharData data : rowData) {
                if (data.isSelected()) {
                    sb.append(data.getC());
                }
            }
        }
        return sb.toString();
    }

    // 获取书籍ID
    public int getBookId() {
        return bookId;
    }

    // 获取书籍路径
    public String getBookPath() {
        return bookPath;
    }

    // 获取书名
    public String getBookName() {
        return book.getName();
    }

    // 获取本章目录
    public String getCatalogName() {
        return catalogName;
    }

    // 获取编码
    public String getCharset() {
        return configDao.getCharset(bookPath);
    }

    // 清空本页文字字符数据
    public void cleanPageCharDatas() {
        pageCharDatas.clear();
    }

    // 设置背景色
    public void setBgColor(int bgColor) {
        this.bgColor = bgColor;
    }

    // 获取背景色
    public int getBgColor() {
        return bgColor;
    }

    // 本次播放的文字行集合中的下标
    public void setTtsPlayLineIndexs(List<Integer> ttsPlayLineIndexs) {
        this.ttsPlayLineIndexs = ttsPlayLineIndexs;
    }

    // 设置内容View
    public void setContentView(BookContentView contentView) {
        this.contentView = contentView;
    }

    // 设置编码
    public void setCharset(String charset) {
        this.charset = charset;
        // 更新缓存中的编码
        ConfigDao.getInstance().setCharset(bookPath, Constants.DEFAULT_CHARSET);
    }

    // 本次是否取消翻页
    public boolean isCancelPage() {
        return isCancelPage;
    }

    // 取消翻页时，将当前页面还原到存储的上一次的页面
    public void cancelPage() {
        cleanPageCharDatas();
        isCancelPage = true;
        currentPageVo.setLines(tempPageVo.getLines());
        currentPageVo.setMbbStart(tempPageVo.getMbbStart());
        currentPageVo.setMbbEnd(tempPageVo.getMbbEnd());
    }

    // 翻页时，将当前页面信息存储到临时页面
    public void saveTempPageVo() {
        isCancelPage = false;
        tempPageVo.setLines(currentPageVo.getLines());
        tempPageVo.setMbbStart(currentPageVo.getMbbStart());
        tempPageVo.setMbbEnd(currentPageVo.getMbbEnd());
    }

    // 停止播放isActStop:是否真正停止，并丢失焦点
    public void stopPlay(boolean isActStop) {
        if (contentView != null) {
            contentView.stopPlay(isActStop);
        }
    }

    // 取消倒计时
    public void cancelTimer() {
        if (contentView != null) {
            contentView.cancelTimer();
        }
    }

    // 开始播放
    public Boolean startPlay() {
        Ahoi.getAuditFocus();
        return contentView != null && contentView.startPlay();
    }

    // 是否正在播放
    public boolean isPlaying() {
        return contentView != null && contentView.isPlaying();
    }

    // 开始播放，仅当未播放时
    public boolean startPlayIfNotPlay() {
        if (!isPlaying()) {
//            Ahoi.getAuditFocus();
            return startPlay();
        }
        return true;
    }

    // 是否继续播放
    public boolean isContinueToPlay() {
        return Constants.continueToPlay;
    }

    // 刷新页面内容
    public void refresh(Canvas current, Canvas next) {
        isCancelPage = false;
        doDraw(current);
        doDraw(next);
    }

    // 跳转到下一章
    public Integer doPageNextCatalog() {
        Integer position = catalogDao.getNextPosition(bookId);
        if (position == null) {
            ToastUtil.show("没有下一章了~");
        } else {
            contentView.doPageNext(position);
        }
        return position;
    }

    // 跳转到上一章
    public Integer doPagePrevCatalog() {
        Integer position = catalogDao.getPrevPosition(bookId);
        if (position == null) {
            ToastUtil.show("没有上一章了~");
        } else {
            contentView.doPageNext(position);
        }
        return position;
    }

}
